#pragma once

#ifndef __PHYSICS__PREREQS__INCLUDED__HH__
#define __PHYSICS__PREREQS__INCLUDED__HH__

#include <Newton.h>
#include <LitPrerequisites.h>
#include <LitPointer.hpp>
#include "LitPhysicsTypes.h"

#define PHYSICS_GRAVITY -9.81f
#define USE_VISUAL_DEBUGGER

#define PHYSICS_FIXED_FPS			Lit::Real(120.0f)
#define PHYSICS_FRAME_TIME			Lit::Real(1000.0f/PHYSICS_FIXED_FPS)
#define PHYSICS_MAX_LOOPS			1

namespace Lit
{
	namespace Physics
	{
		typedef Lit::RefPtr<NewtonBody>			_BodyPtr;
		typedef Lit::RefPtr<NewtonWorld>		_WorldPtr;
		typedef Lit::RefPtr<NewtonCollision>	_CollisionPtr;
		class Body;
		class CollisionShape;
		class World;
		class BodyState;
		class VisualDebugger;

		class LIT_API Matrix
		{
			Lit::Mat4		mMatrix;
			float			mNMat[4][4];
		public:
			Matrix( const Lit::Vec3 & pos, const Lit::Quat& orient = Lit::Quat::IDENTITY );
			Matrix( float *mat );
			Matrix();
			
			Matrix& operator=( float *mat );
			Matrix& operator=( const Lit::Mat4& mat );
			Matrix& operator=( const Matrix& mat );

			//! Take a Newton matrix and create a Quaternion + Position_vector
			static void MatrixToQuatPos( const float* matrix, Ogre::Quaternion& quat, Ogre::Vector3 &pos );
			//! Take a Quaternion and Position Matrix and create a Newton-happy float matrix!
			static void QuatPosToMatrix( const Ogre::Quaternion& quat, const Ogre::Vector3 &pos, float* matrix );
			static Matrix NewtonToOgre( const float* matrix_in );
			// Matrix4x4
			static void OgreToNewton( const Ogre::Matrix4& matrix_in, float* matrix_out );
			
			float* toNewton( );

			Lit::Vec3 getPosition() const;
			Lit::Quat getOrientation() const;

			void setPosition( const Lit::Vec3 &pos );
			void setOrientation( const Lit::Quat& orient );
		};
		struct LIT_API MassMatrix
		{
			MassMatrix( const MassMatrix& rk ){ mass = rk.mass; inertia=rk.inertia; }
			MassMatrix( Lit::Real _mass, const Lit::Vec3& _inertia=Lit::Vec3::ZERO ) : mass(_mass), inertia(_inertia) {}
			MassMatrix( ) : mass(0.0f), inertia(Lit::Vec3::ZERO) {}
			virtual ~MassMatrix(){}

			// scaled by mass
			Lit::Vec3 scaledInertia() const{ return inertia * mass; }

			float mass;
			Lit::Vec3 inertia;
		};
		struct LIT_API InertialMatrix : public MassMatrix
		{
			InertialMatrix( const MassMatrix& rk, const Lit::Vec3 &origin_ = Lit::Vec3::ZERO ) : MassMatrix(rk){ origin=origin_; }
			InertialMatrix( const InertialMatrix& rk ) : MassMatrix(rk){ origin=rk.origin; }
			InertialMatrix( Lit::Real _mass, const Lit::Vec3& _inertia=Lit::Vec3::ZERO , const Lit::Vec3 & _origin=Lit::Vec3::ZERO) : MassMatrix( _mass, _inertia ), origin(_origin) {}
			InertialMatrix( ) : MassMatrix(), origin(Lit::Vec3::ZERO) {}

			Lit::Vec3 origin;
		};
		enum ForceFlags
		{
			USES_NOTHING = 0,
			USES_FORCE = 1<<1,
			USES_TORQUE = 1<<2,
			USES_VELOCITY = 1<<3,
			USES_ALL = USES_TORQUE|USES_VELOCITY|USES_FORCE
		};

		class LIT_API Force
		{
		public:

			// composite is direction * force
			// force is composite's length
			// direction is composite normalised vector
			Force( const Lit::Vec3& force );
			Force( const Lit::Vec3& direction, Lit::Real force, Lit::Real velocity = 0.0f, Lit::Real torque = 0.0f, size_t flags = USES_ALL );
			Force( const Force &rk );
			Force( );
			~Force();
			

			Force& operator=( const Force &rk );

			bool operator==( const Force& rk ) const;
			bool operator!=( const Force& rk ) const;
			

			bool hasForce() const;
			bool hasVelocity() const;
			bool hasTorque() const;
			bool hasNothing() const;

			size_t getFlags() const;
			bool hasFlag( ForceFlags f ) const;
			void setFlags( size_t flags );
			
			void reset();
			void set( const Lit::Vec3& direction = Lit::Vec3::ZERO, Lit::Real force = 0.0f, Lit::Real velocity = 0.0f, Lit::Real torque = 0.0f, size_t flags = USES_ALL );
			void setComposed( const Lit::Vec3& direction_mul_force );

			void setDirection( const Lit::Vec3& dir );
			void setForce( Lit::Real fo );
			void setVelocity( Lit::Real v );
			void setTorque( Lit::Real t );

			const Lit::Vec3 &getDirection() const;
			Lit::Real getVelocity() const;
			Lit::Real getTorque() const;
			Lit::Real getForce() const;
			
			////////////////////////////////////
			//		Direction * scalar
			//////
			Lit::Vec3 getRealVelocity() const;
			Lit::Vec3 getRealTorque() const;
			Lit::Vec3 getRealForce() const;
			
			////////////////////////////////////////////////
			//		Direction * scalar * body's mass
			//////
			Lit::Vec3 getAppliedVelocity( float mass ) const;
			Lit::Vec3 getAppliedTorque( float mass ) const;
			Lit::Vec3 getAppliedForce( float mass ) const;

			static const Force	ZERO;
			
		protected:
			size_t		mFlags;
			Lit::Vec3	mDirection;
			Lit::Real	mVelocity;
			Lit::Real	mTorque;
			Lit::Real	mForce;

		};

		LIT_API std::ostream& operator << ( std::ostream & strm, const Force& f );
		struct LIT_API ForceList
		{
		public:
			struct LIT_API iterator;
			struct LIT_API const_iterator;
			typedef std::list<Force> type;

			friend struct iterator;
			friend struct const_iterator;

		protected:
			type _d;
			
		public:
			ForceList();
			ForceList( const ForceList& rk );
			~ForceList();

			void clear();
			ForceList& push( const Force& f );
			ForceList& pop();

			type& data();
			const type& data() const;
			
			iterator begin();
			const_iterator begin() const;

			iterator end();
			const_iterator end() const;

			bool empty();
			size_t count() const;
			size_t size() const;
			
			struct Result
			{
				Result(){ force = veloc = torque = Lit::Vec4::ZERO; has_force=has_veloc=has_torque=false; }
				~Result(){}

				Lit::Vec4 force;
				Lit::Vec4 veloc;
				Lit::Vec4 torque;

				bool has_force;
				bool has_veloc;
				bool has_torque;
			};
			Result calculateExternalForces( const Body * body );
		};
		struct LIT_API ForceList::iterator
		{
			typedef ForceList::type data_t;
			typedef data_t::iterator itor_t;

			iterator( data_t& d, itor_t it ) : data(d), itor( it ){}
			iterator( const iterator& rk ):data(rk.data), itor(rk.itor){}

			iterator& operator =( const iterator& rk ){ data = rk.data; itor = rk.itor; return *this; }

			iterator operator++() const{ iterator i(*this); return i.advance(); }
			iterator& operator++( const int ){ itor ++; return *this; }

			bool operator == ( const iterator & rk ) const{ return itor == rk.itor; }
			bool operator != ( const iterator & rk ) const{ return itor != rk.itor; }

			bool operator == ( const itor_t & rk ) const{ return itor == rk; }
			bool operator != ( const itor_t & rk ) const{ return itor != rk; }

			bool atEnd() const{ return itor==data.end(); }
			iterator& advance(){ assert(itor != data.end() && "Invalid force iterator, already at end"); itor++; return *this; }

			data_t::value_type* operator->(){ return &*itor; }
			data_t::value_type* operator*(){ return &*itor; }

			data_t& data;
			itor_t itor;
		};
		struct LIT_API ForceList::const_iterator
		{
			typedef ForceList::type data_t;
			typedef data_t::const_iterator itor_t;

			const_iterator( const data_t& d, itor_t it ) : data(d), itor( it ){}
			const_iterator( const const_iterator& rk ):data(rk.data), itor(rk.itor){}

			const_iterator& operator =( const const_iterator& rk ){ data = rk.data; itor = rk.itor; return *this; }

			const_iterator operator++() const{ const_iterator i(*this); return i.advance(); }
			const_iterator& operator++( const int ){ itor ++; return *this; }

			bool atEnd() const{ return itor==data.end(); }
			const_iterator& advance(){ assert(itor != data.end() && "Invalid force iterator, already at end"); itor++; return *this; }
			
			const data_t::value_type* operator->() const{ return &*itor; }
			const data_t::value_type* operator*() const{ return &*itor; }
			
			bool operator == ( const const_iterator & rk ) const{ return itor == rk.itor; }
			bool operator != ( const const_iterator & rk ) const{ return itor != rk.itor; }

			bool operator == ( const itor_t & rk ) const{ return itor == rk; }
			bool operator != ( const itor_t & rk ) const{ return itor != rk; }

			data_t data;
			itor_t itor;
		};
	};
};
#endif